using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Windows.Shapes;
using System.Windows;

namespace OnlinePlots.Diagrams
{
    /// <summary>
    /// Represent an abstract diagram element
    /// </summary>
    public class DiagramElement : DependencyObject, INotifyPropertyChanged
    {
        protected DiagramElement parent;
        protected List<DiagramElement> children = new List<DiagramElement>();
        
        protected List<UIElement> shapes = new List<UIElement>();
        protected DiagramControl diagram;

        protected DiagramElement()
        {
        }

        internal DiagramElement(DiagramControl diagram)
            :this()
        {
            this.diagram = diagram;
        }

        public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register(
                "Visible",
                typeof(bool),
                typeof(DiagramElement),
                new PropertyMetadata(true)
            );

        /// <summary>
        /// Gets or sets the visibility flag of the diagram element
        /// </summary>
        [Description("Visibility flag of the diagram element")]
        public Boolean Visible
        {
            get { return (bool)GetValue(VisibleProperty); }
            set { SetValue(VisibleProperty, value); }
        }

        public static DependencyProperty ZOrderProperty = DependencyProperty.Register(
                "ZOrder",
                typeof(int),
                typeof(DiagramElement),
                new PropertyMetadata(1, new PropertyChangedCallback(OnZOrderPropertyChanged))
            );

        /// <summary>
        /// Gets or sets the z-order of the diagram element
        /// </summary>
        [Description("Z-order value of the diagram element")]
        public int ZOrder
        {
            get { return (int)GetValue(ZOrderProperty); }
            set { SetValue(ZOrderProperty, value); }
        }

        static protected void OnZOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("ZOrder");
        }

        /// <summary>
        /// Gets the diagram to which the element belongs
        /// </summary>
        public DiagramControl Diagram
        {
            get { return diagram; }
        }

        /// <summary>
        /// Gets the direct parent of the element
        /// </summary>
        public DiagramElement Parent
        {
            get { return parent; }
        }

        /// <summary>
        /// Gets the list of child emenets of the diagram element
        /// </summary>
        public List<DiagramElement> Children
        {
            get { return children; }
        }

        /// <summary>
        /// Gets the list of shapes associated with the element
        /// </summary>
        public List<UIElement> Shapes
        {
            get { return shapes; }
        }

        internal void BindToDiagramElement(DiagramElement diagramElement)
        {
            parent = diagramElement;
            diagram = diagramElement.diagram;
            if (!diagramElement.children.Contains(this))
            {
                diagramElement.children.Add(this);
            }
        }

        internal void Unbind()
        {
            if (parent != null)
            {
                parent.children.Remove(this);
                parent = null;
            }
            diagram = null;            
        }

        internal List<UIElement> GetAllShapes()
        {
            List<UIElement> result = new List<UIElement>();
            result.AddRange(shapes);
            foreach (DiagramElement childElement in children)
            {
                result.AddRange(childElement.GetAllShapes());
            }
            return result;
        }

        internal virtual void ClearAllShapes()
        {
            shapes.Clear();
            foreach (DiagramElement childElement in children)
            {
                childElement.ClearAllShapes();
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
